#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: query.py
# Author: Tomás Vírseda
# License: GPL v3
# Description:  Query Manager. A class for asking Vazaar
from sets import Set
from datetime import datetime

from rdflib import URIRef, Literal
from rdflib.Graph import Graph
from rdflib.Graph import ConjunctiveGraph
from rdflib.sparql.sparqlGraph import SPARQLGraph
from rdflib.sparql.graphPattern import GraphPattern
from rdflib.sparql import Query

from vazaarlib.namespaces import *

s = 0
p = 1
o = 2

class QueryManager:
    """Query Manager class"""

    def __init__(self, graph):
        self.graph = graph
        self.total = 0
        self.cols = 0
        self.clipboard = 0
        self.notes = 0
        self.images = 0
        self.audio = 0
        self.video =  0
        self.text = 0
        self.apps = 0
        self.website = 0
        self.remote = 0
        self.feed = 0
        self.folder = 0


    def collections_linked_to_resource(self, id):
        select = ("?col")
        where = GraphPattern([
            ("?col", NIE['hasLogicalPart'], URIRef(id))
        ])
        sparqlGrph = SPARQLGraph(self.graph)
        res = Query.query(sparqlGrph, select, where, initialBindings=None)
        try:
            return res
        except:
            return None

    def this(self, query, mygraph=None):
        try:
            if mygraph is None:
                #print "asking all graph"
                return self.graph.query(query)
            else:
                #print "asking subgraph"
                return mygraph.query(query)
        except:
            return None

    def how_many_nodes(self):
        query="""
        SELECT DISTINCT ?id
        WHERE {
            ?id ?p ?o .
        }
        """

        result = self.graph.query(query)

        return len(result.selected)

    def resources_by_extension(self, fileExtension=''):
        query="""
        SELECT ?id
        WHERE {
            ?id nfo:fileExtension %s .
        }
        """ % fileExtension

    def get_properties(self, id):
        prop = {}

        for predicate, object in self.graph.predicate_objects(URIRef(id)):
            #property[predicate] = object
            try:
                objects = prop[predicate]
                objects.append(object)
                prop[predicate] = objects
            except:
                objects = []
                objects.append(object)
                prop[predicate] = objects


        return prop


    def get_basic_info(self, id):
        prop = self.get_properties(id)
        #print "---- property list ----"
        #print prop
        basic = []
        basic.append(id)
        try:
            basic.append(prop[NIE['mimeType']])
        except Exception, error:
            basic.append('collection')

        basic.append(prop[NAO['lastModified']])
        basic.append(prop[NIE['title']])

        return basic

    def get_url_from_resource(self, id):
        select = ("?object")
        where = GraphPattern([
            (URIRef(id), RDF['type'], "?object")
        ])
        sparqlGrph = SPARQLGraph(self.graph)
        res = Query.query(sparqlGrph, select, where, initialBindings=None)
        try:
            return str(res[0])
        except:
            return None

    def get_total_resources(self, graph=None):
        if graph is None:
            graph = self.graph

        self.total = len(list(graph.triples((None, RDF.type, None))))

        return self.total

    def get_total_collections(self, graph):
        self.cols = len(list(self.graph.triples((None, RDF.type, PIMO['Collection']))))
        return self.cols

    def get_total_singles(self, graph):
        singles = self.total - self.cols
        if singles < 0:
            singles = 0

        return singles

    def get_total_clipboard(self, graph):
        if graph:
            self.clipboard = len(list(graph.triples((None, RDF.type, NFO['Clipboard']))))
        else:
            self.clipboard = len(list(self.graph.triples((None, RDF.type, NFO['Clipboard']))))
        return self.clipboard

    def get_total_images(self, graph):
        if graph:
            self.images = len(list(graph.triples((None, RDF.type, NFO['Image']))))
        else:
            self.images = len(list(self.graph.triples((None, RDF.type, NFO['Image']))))
        return self.images

    def get_total_audio(self, graph):
        if graph:
            self.audio = len(list(graph.triples((None, RDF.type, NFO['Audio']))))
        else:
            self.audio = len(list(self.graph.triples((None, RDF.type, NFO['Audio']))))
        return self.audio

    def get_total_videos(self, graph):
        if graph:
            self.video = len(list(graph.triples((None, RDF.type, NFO['Video']))))
        else:
            self.video = len(list(self.graph.triples((None, RDF.type, NFO['Video']))))
        return self.video

    def get_total_text(self, graph):
        if graph:
            self.text = len(list(graph.triples((None, RDF.type, NFO['TextDocument']))))
        else:
            self.text = len(list(self.graph.triples((None, RDF.type, NFO['TextDocument']))))
        return self.text

    def get_total_website(self, graph):
        if graph:
            self.website = len(list(graph.triples((None, RDF.type, NFO['Website']))))
        else:
            self.website = len(list(self.graph.triples((None, RDF.type, NFO['Website']))))
        return self.website

    def get_total_feed(self, graph):
        if graph:
            self.feed = len(list(graph.triples((None, RDF.type, NFO['Feed']))))
        else:
            self.feed = len(list(self.graph.triples((None, RDF.type, NFO['Feed']))))
        return self.feed

    def get_total_remote(self, graph):
        if graph:
            self.remote = len(list(self.graph.triples((None, RDF.type, NFO['RemoteDataObject']))))
        else:
            self.remote = len(list(graph.triples((None, RDF.type, NFO['RemoteDataObject']))))
        return self.remote

    def get_total_application(self, graph):
        if graph:
            self.apps = len(list(graph.triples((None, RDF.type, NFO['Application']))))
        else:
            self.apps = len(list(self.graph.triples((None, RDF.type, NFO['Application']))))
        return self.apps

    def get_total_folder(self, graph):
        if graph:
            self.folder = len(list(graph.triples((None, RDF.type, NFO['Folder']))))
        else:
            self.folder = len(list(self.graph.triples((None, RDF.type, NFO['Folder']))))
        return self.folder

    def get_total_other(self, graph):
        pass
        #return (self.singles - (self.clipboard + self.images + self.notes + self.audio + self.video + self.text + self.apps))

    def get_total_notes(self, graph):
        return 0
        #return len(list(self.graph.triples((None, RDF.type, NFO['Note'])))) # FIXME. Wrong

    def get_resources_by_property(self, store, filter, value):
        ns, qname = filter.split(':')
        property = URIRef(NSBINDINGS[ns][qname])
        if ns == 'rdf':
            value = URIRef(value)
        else:
            value = Literal(value)
        #print
        #print filter, ns, qname, property, value
        #print
        select = ("?id")
        where = GraphPattern([
            ("?id", property, value)
        ])

        sparqlGrph = SPARQLGraph(self.graph)
        resources = Query.query(sparqlGrph, select, where, initialBindings=None)

        return resources

    def get_resources_by_extension(self, ext=""):
        select = ("?id", "?type", "?title")
        where = GraphPattern([
            ("?id", RDF.type, "?type"),
            ("?id", NIE['title'], "?title"),
            ("?id", NFO['mimetype'], "?type"),
            ("?id", NFO['extension'], Literal(ext)),
            ("?id", NFO['scheme'], Literal('file'))
        ])
        sparqlGrph = SPARQLGraph(self.graph)
        resources = Query.query(sparqlGrph, select, where, initialBindings=None)

        return resources

    def resource_is_collection(self, id):
        select = ("?res")
        where = GraphPattern([
            (URIRef(id), RDF.type, PIMO['Collection']),
            (URIRef(id), NIE['hasLogicalPart'], "?res"),
        ])
        sparqlGrph = SPARQLGraph(self.graph)
        resources = Query.query(sparqlGrph, select, where, initialBindings=None)

        return list(resources)

    def unlinked_resources(self):
        all = Set()
        linked = Set()
        cols = Set()
        unlinked = Set()

        for triple in self.graph.triples((None, RDF['type'], None)):
            all.add(triple[s])

        for triple in self.graph.triples((None, NIE['hasLogicalPart'], None)):
            linked.add(triple[o])

        for triple in self.graph.triples((None, RDF['type'], PIMO['Collection'])):
            cols.add(triple[s])

        unlinked = all - linked - cols

        return unlinked


    def resources_in_collection(self, col):
        triples = self.graph.triples((URIRef(col), NIE['hasLogicalPart'], None))
        resources = []

        for triple in triples:
            resources.append(triple[o])

        return resources

    """"
    def resources_in_collection(self, id):
        triples = self.graph.triples((URIRef(id), NIE['hasLogicalPart'], None))
        resources = []
        for triple in triples:
            basic = self.get_basic_info(triple[o])
            id = basic[0]
            icon = basic[1]
            name = basic[2]
            date = basic[3]
            resource = (id, icon, name, date)
            resources.append(resource)

        return resources
    """

    def resources_into_collection(self, id):
        query="""

        SELECT DISTINCT ?id
        WHERE {
            %s %s ?id .
        }
        """ % (id, NIE['hasLogicalPart'])

        result = self.graph.query(query)

        return len(result.selected)

    def get_property(self, id, property):
        select = ("?object")
        where = GraphPattern([
            (URIRef(id), URIRef(property), "?object"),
        ])
        sparqlGrph = SPARQLGraph(self.graph)
        object = Query.query(sparqlGrph, select, where, initialBindings=None)

        try:
            return object[0]
        except Exception, error:
            return error

    def get_tags(self, id):
        select = ("?tag")
        where = GraphPattern([
            (URIRef(id), NAO['hasTag'], "?tag"),
        ])
        sparqlGrph = SPARQLGraph(self.graph)
        tags = Query.query(sparqlGrph, select, where, initialBindings=None)

        return tags


    def get_qname_from_predicate(self, store, predicate):
        namespace, uriref, qname = store.compute_qname(predicate)

        return qname

    def get_namespace_from_predicate(self, store, predicate):
        namespace, uriref, qname = store.compute_qname(predicate)

        return namespace

    def get_namespaces(self, store):
        namespaces = []
        predicates = self.get_predicates(store)

        for predicate in predicates:
            namespace, uriref, qname = store.compute_qname(predicate)
            namespaces.append(namespace)

        return namespaces

    def get_predicates(self, store):
        query ="""
            PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
            PREFIX nie: <http://www.semanticdesktop.org/ontologies/nie/>
            PREFIX nfo: <http://www.semanticdesktop.org/ontologies/nfo/>

            SELECT distinct ?predicate
            WHERE
            {
                ?id ?predicate ?object .
            }
            ORDER BY ?predicate
        """
        result = store.query(query)

        return result.selected


    def get_predicate_qnames(self, store):
        qnames = []
        predicates = self.get_predicates(store)

        for predicate in predicates:
            namespace, uriref, qname = store.compute_qname(predicate)
            qnames.append(qname)

        return qnames

    def get_qname_frequency(self, store, predicate):
        relations = list(store.subject_objects(predicate))
        freq = {}
        for s, o in relations:
            try:
                count = freq[str(o)]
                freq[str(o)] = count + 1
            except Exception, e:
                freq[str(o)] = 1

        return freq

    def get_qname_from_predicate(self, store, predicate):
        namespace, uriref, qname = store.compute_qname(predicate)

        return qname

    def get_namespace_from_predicate(self, store, predicate):
        namespace, uriref, qname = store.compute_qname(predicate)

        return namespace

    def make_report(self, store=None):
        if not store:
            store = self.graph

        t1 = datetime.now()
        report = {}

        for namespace in self.get_namespaces(store):
            report[str(namespace)] = {}

        for predicate in self.get_predicates(store):
            ns = self.get_namespace_from_predicate(store, predicate)
            qname = self.get_qname_from_predicate(store,predicate)
            frequency = self.get_qname_frequency(store, predicate)
            report[str(ns)][str(qname)] = frequency

        t2 = datetime.now()
        #print "\t\tmaking report: %s" % (t2 - t1)

        return report

    def create_temporal_graph(self, resources):
        graph = ConjunctiveGraph()
        nodes = []
        for resource in resources:
            triples = self.graph.triples((URIRef(resource), None, None))
            for triple in triples:
                graph.add(triple)

        """Bind namespaces to graph"""
        for ns in NSBINDINGS:
            graph.bind(ns, NSBINDINGS[ns])

        return graph

    def tags(self, id):
        triples = self.graph.triples((URIRef(id), NAO['hasTag'], None))
        rtags = []
        for triple in triples:
            rtags.append(triple[2])
        #print rtags
        return rtags

    def describe(self, id):
        rid = URIRef(id)
        rgraph = self.create_temporal_graph((rid,))
        print rgraph.serialize(format='pretty-xml')
